---
title: Провайдеры
---

Теперь, когда мы установили [Riverpod], давайте поговорим о провайдерах.

Провайдеры - самая важная составляющая [Riverpod] приложения.
Провайдер - объект, который инкапсулирует часть состояния и позволяет наблюдать
за этим состоянием.

## Зачем использовать провайдеры?

Инкапсуляция части состояния в провайдере:

- Предоставляет легкий доступ к этому состоянию из разных мест.
  Провайдеры являются полной заменой таким паттернам, как Singleton,
  Service Locator, Dependency Injection и InheritedWidget.

- Упрощает совместное использование данного состояния с другими.
  Пытались когда-нибудь объединить несколько объектов в один? Данный сценарий
  реализован внутри провайдеров.

- Повышение производительности.
  Отслеживание перестроек виджетов или кэширование результатов затратных вычислений.
  Провайдеры гарантируют, что только изменение состояния вызывает перестойку/перевычисление.

- Повышение тестируемости вашего приложения.
  С провайдерами вам больше не нужны `setUp`/`tearDown`. Более того, можно изменить
  поведение любого провайдера во время теста, что позволяет легко протестировать
  конкретное поведение.

- Простое внедрение дополнительное функционала, как логгирование или pull-to-refresh.

## Создание провайдера

Существует множество различных провайдеров, но все они работают одинаково.

Самый распространенный вариант использования это объявление провайдера в виде
глобальной константы:

```dart
final myProvider = Provider((ref) {
  return MyValue();
});
```

:::note
Не переживайте, что провайдеры создаются как глобальные переменные.
Провайдеры полностью неизменяемы. Объявление провайдера ничем не отличается
от объявления функции. Провайдеры поддаются тестированию и сопровождению.
:::

Данный отрывок кода состоит из трех частей:

- `final myProvider` - объявление переменной.
  В дальнейшем мы будем использовать ее для чтения состояния нашего провайдера.
  Провайдеры всегда должны быть объявлены с `final`.

- `Provider` - тип провайдера, который мы решили использовать.
  [Provider] самый простой провайдер. Он предоставляет объект,
  который никогда не изменится.
  Мы можем заменить [Provider] другим провайдером, например [StreamProvider] или
  [StateNotifierProvider], для иного взаимодействия со значением.

- Функция, которая создает состояние.
  Эта функция всегда получает `ref` в качестве параметра. `ref` дает нам
  возможность читать другие провайдеры, выполнять какие-либо операции при
  аннулировании состояния нашего провайдера и т. п.

Тип возвращаемого значения данной функции определяется типом провайдера, который
мы используем.
Например, для [Provider] функция может создавать объект любого типа.
Однако для [StreamProvider] функция должна возвращать [Stream].

:::info
Вы можете объявить сколько угодно провайдеров без ограничения.
[Riverpod], в отличие от `package:provider`, позволяет создавать несколько
провайдеров, хранящих один и тот же тип:

```dart
final cityProvider = Provider((ref) => 'London');
final countryProvider = Provider((ref) => 'England');
```

Два провайдера со значением типа `String` не вызовут ошибку.
:::

:::caution
Для работы с провайдерами мы должны добавить [ProviderScope] в корень
вашего Flutter приложения:

```dart
void main() {
  runApp(ProviderScope(child: MyApp()));
}
```

:::

## Разнообразие провайдеров

Существует множество разных провайдеров для различных случаев применения.

С таким большим набором провайдеров иногда бывает сложно определить, какой
тип предпочтительней использовать.
Чтобы выбрать подходящий провайдер, воспользуйтесь данной таблицей.

| Тип Провайдера           | Функция создания провайдера          | Пример использования                                                     |
| ------------------------ | ------------------------------------ | ------------------------------------------------------------------------ |
| [Provider]               | Возвращает любой тип                 | Класс сервиса / вычисленное значение (отфильтрованный список)            |
| [StateProvider]          | Возвращает любой тип                 | Условие фильтрации / простейший объект состояния                         |
| [FutureProvider]         | Возвращает Future любого типа        | Результат обращения к API                                                |
| [StreamProvider]         | Возвращает Stream любого типа        | Stream результатов, полученных с API                                     |
| [StateNotifierProvider]  | Возвращает подкласс StateNotifier    | Сложный объект состояния, который можно изменить только через интерфейс  |
| [ChangeNotifierProvider] | Возвращает подкласс ChangeNotifier   | Сложный объект состояния, который можно изменить напрямую                |

:::caution
Не смотря на то что каждый провайдер имеет свое предназначение, [ChangeNotifierProvider] не рекомендуется
использовать в масштабируемых приложениях из-за проблем с изменяемым состоянием.
Этот провайдер есть в пакете `flutter_riverpod` для легкой миграции с `package:provider`, также он необходим
для специфичных случаев, таких как взаимодействие с пакетами Navigator 2.
:::

## Модификаторы провайдера

Все провайдеры имеют встроенную возможность расширения функционала.

Можно добавить новые свойства к объекту `ref` или же слегка изменить чтение провайдера.
Модификаторы можно использовать с любым провайдером. Синтаксис их использования
похож на именованный конструктор:

```dart
final myAutoDisposeProvider = StateProvider.autoDispose<int>((ref) => 0);
final myFamilyProvider = Provider.family<String, int>((ref, id) => '$id');
```

На данный момент существует лишь два модификатора:

- [`.autoDispose`](/docs/concepts/modifiers/auto_dispose), который автоматически аннулирует состояние провайдера, если он больше
  никем не прослушивается.
- [`.family`](/docs/concepts/modifiers/family), который позволяет создать провайдер с использованием дополнительных параметров.

:::note
Можно применить сразу несколько модификаторов к одному провайдеру:

```dart
final userProvider = FutureProvider.autoDispose.family<User, int>((ref, userId) async {
  return fetchUser(userId);
});
```

:::


На этом заканчивается данный гайд!

Также вы можете изучить [Как читать провайдер](/docs/concepts/reading).
Посмотрите еще [Как комбинировать провайдеры](/docs/concepts/combining_providers).

[get_it]: http://pub.dev/packages/get_it
[inheritedwidget]: https://api.flutter.dev/flutter/widgets/InheritedWidget-class.html
[stream]: https://api.dart.dev/stable/2.8.4/dart-async/Stream-class.html
[ondispose]: https://pub.dev/documentation/riverpod/latest/riverpod/Ref/onDispose.html
[riverpod]: https://github.com/rrousselgit/riverpod
[hooks_riverpod]: https://pub.dev/packages/hooks_riverpod
[flutter_riverpod]: https://pub.dev/packages/flutter_riverpod
[flutter_hooks]: https://github.com/rrousselGit/flutter_hooks
[provider]: /docs/providers/provider
[streamprovider]: /docs/providers/stream_provider
[futureprovider]: /docs/providers/future_provider
[stateprovider]: /docs/providers/state_provider
[statenotifierprovider]: /docs/providers/state_notifier_provider
[changenotifierprovider]: https://pub.dev/documentation/flutter_riverpod/latest/flutter_riverpod/ChangeNotifierProvider-class.html

[providerscope]: https://pub.dev/documentation/flutter_riverpod/latest/flutter_riverpod/ProviderScope-class.html
